// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.springframework.asm;

/**
 * 一个非标准的类、字段、方法或代码属性，如《Java虚拟机规范》（JVMS）所定义。
 *
 * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
 *     4.7</a>
 * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS
 *     4.7.3</a>
 * @author Eric Bruneton
 * @author Eugene Kuleshov
 */
public class Attribute {

  /** 该属性的类型，在JVMS中也称为其名称。 */
  public final String type;

  /**
   *
   * 这个属性的原始内容，只用于未知属性（见{@link #isUnknown()}）。
   * 该属性的6个头字节（attribute_name_index和attribute_length）是<i>not</i>的。
   * 包括在内。
   *
   */
  private byte[] content;

  /**
   * 这个属性列表中的下一个属性（属性实例可以通过这个字段链接，以存储一个类、字段、方法或代码属性的列表）。)
   * 可以是{@literal null}。
   */
  Attribute nextAttribute;

  /**
   * 构建一个新的空属性。
   *
   * @param type 属性的类型。
   */
  protected Attribute(final String type) {
    this.type = type;
  }

  /**
   *  如果该类型的属性是未知的，则返回{@literal true}。
   *  这意味着，该属性内容不能被解析以提取常量池引用、标签等。
   *  相反，该属性内容被读成一个不透明的字节数组，并按原样写回去。
   *  这可能会导致无效的属性，如果该内容实际上包含常量池引用、标签或其他符号引用，
   *  当常量池、方法字节码等发生变化时，需要对其进行更新。
   *  方法的字节码等发生变化时，需要更新这些内容。这个方法的默认实现总是返回{@literal true}。
   *
   * @return {@literal true}如果这种类型的属性是未知的。
   */
  public boolean isUnknown() {
    return true;
  }

  /**
   *  如果该类型的属性是一个代码属性，则返回{@literal true}。
   *
   * @return {@literal true}如果这种类型的属性是一个代码属性。
   */
  public boolean isCodeAttribute() {
    return false;
  }

  /**
   * 返回与此属性对应的标签。
   *
   * @return 该属性对应的标签，如果该属性不是包含标签的代码属性，则{@literal null}。
   */
  protected Label[] getLabels() {
    return new Label[0];
  }

  /**
   * 读取一个{@link #type}属性。这个方法必须返回一个<i>new</i> {@link Attribute}对象。
   * 类型为{@link #type}，对应于从'offset'开始的'length'字节，在给出的ClassReader。
   *
   * @param classReader 包含要读取的属性的类。
   * @param offset 属性内容的第一个字节在{@link ClassReader}中的索引。
   *               6个属性头的6个字节（attribute_name_index和attribute_length）没有被考虑在内。
   *               在此考虑。
   * @param length 属性内容的长度（不包括6个属性头的字节）。
   * @param charBuffer 用来调用需要'charBuffer'参数的ClassReader方法的缓冲区。
   * @param codeAttributeOffset 在{@link ClassReader}中，Code属性内容的第一个字节的索引。
   *                            在{@link ClassReader}中，如果要读取的属性不是一个代码属性，则为-1。
   *                            这6个属性头的6个字节（attribute_name_index和attribute_length）不会被考虑在内。
   *                            在此考虑。
   * @param labels 方法代码的标签；如果要读取的属性不是Code属性，则为{@literal null}。
   * @return 一个新的 {@link Attribute}对象，对应于指定的字节。
   */
  protected Attribute read(
      final ClassReader classReader,
      final int offset,
      final int length,
      final char[] charBuffer,
      final int codeAttributeOffset,
      final Label[] labels) {
    Attribute attribute = new Attribute(type);
    attribute.content = new byte[length];
    System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
    return attribute;
  }

  /**
   * 返回该属性内容的字节数组形式。6个头的字节(attribute_name_index和attribute_length)
   * 必须<i>not</i>被添加到返回的ByteVector中。
   *
   * @param classWriter 这个属性必须被添加到的类。这个参数可以用来把对应于这个属性的项目添加到这个类的常量池中。
   * @param code 与此Code属性对应的方法的字节码，如果此属性不是Code属性，则{@literal null}。
   *             对应于Code属性的'code'字段。
   * @param codeLength  该代码属性对应的方法的字节码长度，如果该属性不是代码属性，则为0。
   *                    对应于代码属性的'code_length'字段。
   * @param maxStack 这个代码属性对应的方法的最大堆栈大小，如果这个属性不是一个代码属性，则为-1。
   * @param maxLocals  该代码属性对应的方法的最大局部变量数，如果该属性不是代码属性，则为-1。
   * @return 该属性的字节数组形式。
   */
  protected ByteVector write(
      final ClassWriter classWriter,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals) {
    return new ByteVector(content);
  }

  /**
   * 返回属性列表中以该属性开始的属性数量。
   *
   * @return 属性列表中以该属性开始的属性的数量。
   */
  final int getAttributeCount() {
    int count = 0;
    Attribute attribute = this;
    while (attribute != null) {
      count += 1;
      attribute = attribute.nextAttribute;
    }
    return count;
  }

  /**
   * 返回属性列表中以该属性开始的所有属性的总大小（字节）。这个属性。
   * 这个大小包括6个头字节（attribute_name_index和每个属性的6个头字节（attribute_name_index 和 attribute_length）。
   * 同时将属性类型名称添加到常量池中。
   *
   * @param symbolTable 属性中使用的常数必须存储在这里。
   * @return 这个属性列表中所有属性的大小。这个大小包括属性头的大小。
   */
  final int computeAttributesSize(final SymbolTable symbolTable) {
    final byte[] code = null;
    final int codeLength = 0;
    final int maxStack = -1;
    final int maxLocals = -1;
    return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals);
  }

  /**
   * 返回属性列表中以该属性开始的所有属性的总大小（字节）。
   * 这个大小包括每个属性的6个头字节（attribute_name_index 和 attribute_length）。
   * 同时将属性类型名称添加到常量池中。
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
   *     null} if they are not Code attributes. Corresponds to the 'code' field of the Code
   *     attribute.
   * @param codeLength the length of the bytecode of the method corresponding to these code
   *     attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
   *     the Code attribute.
   * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
   *     -1 if they are not Code attributes.
   * @param maxLocals the maximum number of local variables of the method corresponding to these
   *     Code attributes, or -1 if they are not Code attribute.
   * @return the size of all the attributes in this attribute list. This size includes the size of
   *     the attribute headers.
   */
  final int computeAttributesSize(
      final SymbolTable symbolTable,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals) {
    final ClassWriter classWriter = symbolTable.classWriter;
    int size = 0;
    Attribute attribute = this;
    while (attribute != null) {
      symbolTable.addConstantUtf8(attribute.type);
      size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
      attribute = attribute.nextAttribute;
    }
    return size;
  }

  /**
   * 返回对应于给定字段、方法或类的访问标志和签名的所有属性的总大小（字节）。
   * 这个大小包括每个属性的6个头字节（attribute_name_index 和 attribute_length）。
   * 同时将属性类型名称添加到常量池中。
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param accessFlags some field, method or class access flags.
   * @param signatureIndex the constant pool index of a field, method of class signature.
   * @return the size of all the attributes in bytes. This size includes the size of the attribute
   *     headers.
   */
  static int computeAttributesSize(
      final SymbolTable symbolTable, final int accessFlags, final int signatureIndex) {
    int size = 0;
    // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
        && symbolTable.getMajorVersion() < Opcodes.V1_5) {
      // Synthetic attributes always use 6 bytes.
      symbolTable.addConstantUtf8(Constants.SYNTHETIC);
      size += 6;
    }
    if (signatureIndex != 0) {
      // Signature attributes always use 8 bytes.
      symbolTable.addConstantUtf8(Constants.SIGNATURE);
      size += 8;
    }
    // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
      // Deprecated attributes always use 6 bytes.
      symbolTable.addConstantUtf8(Constants.DEPRECATED);
      size += 6;
    }
    return size;
  }

  /**
   * 将属性列表中以该属性开始的所有属性放入给定的字节向量中。
   * 这包括每个属性的6个头字节（attribute_name_index 和 attribute_length）。
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param output where the attributes must be written.
   */
  final void putAttributes(final SymbolTable symbolTable, final ByteVector output) {
    final byte[] code = null;
    final int codeLength = 0;
    final int maxStack = -1;
    final int maxLocals = -1;
    putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output);
  }

  /**
   * 将属性列表中以该属性开始的所有属性放入给定的字节向量中。
   * 这包括6个头字节（attribute_name_index和attribute_length），每个属性。
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
   *     null} if they are not Code attributes. Corresponds to the 'code' field of the Code
   *     attribute.
   * @param codeLength the length of the bytecode of the method corresponding to these code
   *     attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
   *     the Code attribute.
   * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
   *     -1 if they are not Code attributes.
   * @param maxLocals the maximum number of local variables of the method corresponding to these
   *     Code attributes, or -1 if they are not Code attribute.
   * @param output where the attributes must be written.
   */
  final void putAttributes(
      final SymbolTable symbolTable,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals,
      final ByteVector output) {
    final ClassWriter classWriter = symbolTable.classWriter;
    Attribute attribute = this;
    while (attribute != null) {
      ByteVector attributeContent =
          attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
      // Put attribute_name_index and attribute_length.
      output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
      output.putByteArray(attributeContent.data, 0, attributeContent.length);
      attribute = attribute.nextAttribute;
    }
  }

  /**
   * 将对应于给定字段、方法或类的访问标志和签名的所有属性放入给定的字节向量中。
   * 这包括每个属性的6个头字节（attribute_name_index 和 attribute_length）。
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param accessFlags some field, method or class access flags.
   * @param signatureIndex the constant pool index of a field, method of class signature.
   * @param output where the attributes must be written.
   */
  static void putAttributes(
      final SymbolTable symbolTable,
      final int accessFlags,
      final int signatureIndex,
      final ByteVector output) {
    // 在Java 1.5之前，合成字段是用Synthetic属性表示的。
    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
        && symbolTable.getMajorVersion() < Opcodes.V1_5) {
      output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
    }
    if (signatureIndex != 0) {
      output
          .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
          .putInt(2)
          .putShort(signatureIndex);
    }
    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
      output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
    }
  }

  /** 一组属性Set（具有相同类型的属性被认为是相等的）。 */
  static final class Set {

    private static final int SIZE_INCREMENT = 6;

    private int size;
    private Attribute[] data = new Attribute[SIZE_INCREMENT];

    void addAttributes(final Attribute attributeList) {
      Attribute attribute = attributeList;
      while (attribute != null) {
        if (!contains(attribute)) {
          add(attribute);
        }
        attribute = attribute.nextAttribute;
      }
    }

    Attribute[] toArray() {
      Attribute[] result = new Attribute[size];
      System.arraycopy(data, 0, result, 0, size);
      return result;
    }

    private boolean contains(final Attribute attribute) {
      for (int i = 0; i < size; ++i) {
        if (data[i].type.equals(attribute.type)) {
          return true;
        }
      }
      return false;
    }

    private void add(final Attribute attribute) {
      if (size >= data.length) {
        Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
        System.arraycopy(data, 0, newData, 0, size);
        data = newData;
      }
      data[size++] = attribute;
    }
  }
}
